package com.zmn.oms.business.impl.discount;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.zmn.common.constant.FicoConsts;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.math.MoneyUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.reflect.BeanMapper;
import com.zmn.dms.common.dto.coup.dio.coupon.CoupOnceCardCheckDIO;
import com.zmn.dms.common.enums.DiscountTypeEnums;
import com.zmn.dms.dubbo.interfaces.coup.coupon.CoupOnceCardRemoteService;
import com.zmn.dms.dubbo.utils.DubboConsts;
import com.zmn.oms.business.interfaces.discount.OrderDiscountBService;
import com.zmn.oms.business.interfaces.orderamount.OrderAmountCalcBService;
import com.zmn.oms.business.interfaces.orderamount.OrderDiscountCalcBService;
import com.zmn.oms.business.interfaces.servtype.ServItemBService;
import com.zmn.oms.business.interfaces.work.OrderWorkAmountBService;
import com.zmn.oms.common.constant.*;
import com.zmn.oms.common.discount.DiscountFactory;
import com.zmn.oms.common.discount.dto.CalculateDiscountListDTO;
import com.zmn.oms.common.discount.dto.CalculateDiscountResultDTO;
import com.zmn.oms.common.discount.interfaces.DiscountCalculateRuleService;
import com.zmn.oms.common.dto.OmsOperator;
import com.zmn.oms.common.exception.OmsBaseException;
import com.zmn.oms.model.bo.orderamount.OrderAmountCalcBO;
import com.zmn.oms.model.bo.orderamount.OrderDiscountCalcBO;
import com.zmn.oms.model.bo.orderamount.OrderDiscountCompareBO;
import com.zmn.oms.model.bo.orderamount.ReCalcOrderAmountDIO;
import com.zmn.oms.model.dto.discount.ActivityDiscountDTO;
import com.zmn.oms.model.dto.discount.AddDiscountDTO;
import com.zmn.oms.model.dto.order.OrderDTO;
import com.zmn.oms.model.dto.work.workamount.WorkAmountDTO;
import com.zmn.oms.model.entity.detail.OrderDetail;
import com.zmn.oms.model.entity.discount.OrderDiscount;
import com.zmn.oms.model.entity.product.OrderProduct;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.services.interfaces.discount.OrderDiscountService;
import com.zmn.oms.services.interfaces.member.OrderMemberService;
import com.zmn.oms.services.interfaces.work.OrderWorkAmountService;
import com.zmn.oms.services.interfaces.work.OrderWorkService;
import com.zmn.pay.common.constant.PayConsts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 类描述：订单优惠
 *
 * @author heciqi
 * @date 2018/12/10 10:31
 */
@Slf4j
@Service
public class OrderDiscountBServiceImpl implements OrderDiscountBService {

	@Autowired
	private OrderDiscountService orderDiscountService;
	@Autowired
	private OrderWorkAmountService orderWorkAmountService;
	@Autowired
	private OrderWorkService orderWorkService;
	@Autowired
	private OrderWorkAmountBService orderWorkAmountBService;
	@Autowired
	private OrderMemberService orderMemberService;
	@Autowired
	private OrderDiscountCalcBService orderDiscountCalcBService;
	@Autowired
	private OrderAmountCalcBService orderAmountCalcBService;
	@Autowired
	private ServItemBService servItemBService;

	@Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
	CoupOnceCardRemoteService coupOnceCardRemoteService;
	/**
	 * 工程师优惠
	 *
	 * @param addDiscountDTO
	 */
	@Override
	public void masterDiscount(AddDiscountDTO addDiscountDTO) throws OmsBaseException {

		// 原有的工单优惠
		List<CalculateDiscountListDTO> calculateDiscountListDTOS = this.covertOrigDiscounts(addDiscountDTO.getOrderId());

		// 原有的工单优惠上，排除工程师的优惠 和人工优惠
		calculateDiscountListDTOS = calculateDiscountListDTOS.stream()
				.filter(p -> !Objects.equals(p.getCateg(), FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_MASTER)
						&& !Objects.equals(p.getCateg(), FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_OPERATOR))
				.collect(Collectors.toList());

		OrderWork dbOrderWork = orderWorkService.findOrderWorkByKey(addDiscountDTO.getOrderId(), addDiscountDTO.getWorkId());
		// 计算优惠的基础价格原价
		Integer basePrice = dbOrderWork.getOriginalAmount();

		// 工厂方法，得到优惠计算类
		DiscountCalculateRuleService calculateRuleService = DiscountFactory.create(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_MASTER);
		CalculateDiscountResultDTO calculateResultDTO =
				calculateRuleService.calculate(calculateDiscountListDTOS, basePrice, (e) -> addDiscountDTO.getAmount());

		// 1，删除之前人工优惠和工程师优惠
		OrderDiscount deleteDiscount = new OrderDiscount();
		deleteDiscount.setOrderId(addDiscountDTO.getOrderId());
		deleteDiscount.setCateg(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_MASTER);
		orderDiscountService.deleteByOrderIdAndCateg(deleteDiscount);
		deleteDiscount.setCateg(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_OPERATOR);
		orderDiscountService.deleteByOrderIdAndCateg(deleteDiscount);

		// 2，插入工单优惠
		OrderDiscount orderDiscount = new OrderDiscount();
		orderDiscount.setOrderId(addDiscountDTO.getOrderId());
		orderDiscount.setCateg(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_MASTER);
		orderDiscount.setType(calculateResultDTO.getDiscountType());
		orderDiscount.setAmount(calculateResultDTO.getDiscountAmount());
		orderDiscount.setCreater(addDiscountDTO.getDiscountUserName());
		orderDiscount.setRemark(addDiscountDTO.getDiscountUserName());
		orderDiscount.setCreateTime(new Date());
		orderDiscountService.insert(orderDiscount);

		// 3，更新工单优惠金额
		WorkAmountDTO workAmountDTO = new WorkAmountDTO();
		workAmountDTO.setOrderId(addDiscountDTO.getOrderId());
		workAmountDTO.setWorkId(addDiscountDTO.getWorkId());
		workAmountDTO.setAmount(calculateResultDTO.getTotalDiscountAmount());
		orderWorkAmountBService.updateDiscountAmount(workAmountDTO);
	}


	/**
	 * 后台运营优惠
	 *
	 * @param addDiscountDTO
	 */
	@Override
	public void operatorDiscount(AddDiscountDTO addDiscountDTO) throws OmsBaseException {

		// 原有的工单优惠
		List<CalculateDiscountListDTO> calculateDiscountListDTOS = this.covertOrigDiscounts(addDiscountDTO.getOrderId());
		// 原有的工单优惠上，排除工程师的优惠 和人工优惠
		calculateDiscountListDTOS = calculateDiscountListDTOS.stream()
				.filter(p -> !Objects.equals(p.getCateg(), FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_MASTER)
						&& !Objects.equals(p.getCateg(), FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_OPERATOR))
				.collect(Collectors.toList());

		OrderWork dbOrderWork = orderWorkService.findOrderWorkByKeySrcMaster(addDiscountDTO.getOrderId(), addDiscountDTO.getWorkId());
		// 计算优惠的基础价格，为原价
		Integer basePrice = dbOrderWork.getOriginalAmount();

		// 工厂方法，得到优惠计算类
		DiscountCalculateRuleService calculateRuleService = DiscountFactory.create(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_OPERATOR);

		CalculateDiscountResultDTO calculateResultDTO =
				calculateRuleService.calculate(calculateDiscountListDTOS, basePrice, (e) -> (int) addDiscountDTO.getAmount());

		// 1，删除之前人工优惠和工程师优惠
		OrderDiscount deleteDiscount = new OrderDiscount();
		deleteDiscount.setOrderId(addDiscountDTO.getOrderId());
		deleteDiscount.setCateg(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_MASTER);
		orderDiscountService.deleteByOrderIdAndCateg(deleteDiscount);
		deleteDiscount.setCateg(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_OPERATOR);
		orderDiscountService.deleteByOrderIdAndCateg(deleteDiscount);

		// 2，插入工单优惠
		OrderDiscount orderDiscount = new OrderDiscount();
		orderDiscount.setOrderId(addDiscountDTO.getOrderId());
		orderDiscount.setCateg(FicoConsts.ORDER_IN_DISCOUNT_CATEG_ARTIFICIAL_OPERATOR);

		// 判断是否设置为全免优惠
		if (addDiscountDTO.getDiscountFree() == GlobalConsts.YES) {
			orderDiscount.setType(OrderConsts.ORDER_DISCOUNT_TYPE_FREE);
		} else {
			orderDiscount.setType(calculateResultDTO.getDiscountType());
		}

		orderDiscount.setAmount(calculateResultDTO.getDiscountAmount());
		orderDiscount.setCreater(addDiscountDTO.getDiscountUserName());
		orderDiscount.setRemark(StringUtils.defaultString(addDiscountDTO.getDiscountUserName()) + "后台优惠");
		orderDiscount.setCreateTime(new Date());
		orderDiscountService.insert(orderDiscount);

		// 3，更新工单优惠金额
		WorkAmountDTO workAmountDTO = new WorkAmountDTO();
		workAmountDTO.setOrderId(addDiscountDTO.getOrderId());
		workAmountDTO.setWorkId(addDiscountDTO.getWorkId());
		workAmountDTO.setAmount(calculateResultDTO.getTotalDiscountAmount());
		log.info("#oms#operatorDiscount#{} 更新工单优惠金额：{} workAmountDTO：{}", addDiscountDTO.getOrderId(), JSON.toJSONString(workAmountDTO));
		orderWorkAmountBService.updateDiscountAmount(workAmountDTO);
	}

	@Override
	public void activityDiscount(ActivityDiscountDTO activityDiscountDTO) throws OmsBaseException {

		// 1，插入工单优惠券优惠
		OrderDiscount orderDiscount = new OrderDiscount();
		orderDiscount.setOrderId(activityDiscountDTO.getOrderId());
		orderDiscount.setCateg(DiscountConsts.DISCOUNT_CATEG_COUPON);
		orderDiscount.setType(OrderConsts.ORDER_DISCOUNT_TYPE_FULFILL_FREE);
		orderDiscount.setAmount(activityDiscountDTO.getAmount());
		orderDiscount.setSourceId(activityDiscountDTO.getActivityId());
		orderDiscount.setCreater(activityDiscountDTO.getOperator());
		orderDiscount.setRemark(activityDiscountDTO.getActivityName());
		orderDiscount.setCreateTime(new Date());
		orderDiscountService.insert(orderDiscount);

		OrderWork orderWork = orderWorkService.findOrderWorkByKeySrcMaster(activityDiscountDTO.getOrderId(), activityDiscountDTO.getWorkId());
		int totalDiscountAmount = Optional.ofNullable(orderWork.getDiscountAmount()).orElse(0) + activityDiscountDTO.getAmount();

		// 2，更新工单优惠金额
		WorkAmountDTO workAmountDTO = new WorkAmountDTO();
		workAmountDTO.setOrderId(activityDiscountDTO.getOrderId());
		workAmountDTO.setWorkId(activityDiscountDTO.getWorkId());
		workAmountDTO.setAmount(totalDiscountAmount);
		orderWorkAmountBService.updateDiscountAmount(workAmountDTO);
	}

	@Override
	public void channelDiscount(AddDiscountDTO addDiscountDTO) throws OmsBaseException {
		log.info("[{}]插入渠道优惠[{}]", addDiscountDTO.getOrderId(), addDiscountDTO);

		OrderWork orderWork = orderWorkService.findOrderWorkByKeySrcMaster(addDiscountDTO.getOrderId(), addDiscountDTO.getWorkId());
		ReCalcOrderAmountDIO reCalcOrderAmountDIO = orderAmountCalcBService.getReCalcOrderAmountDIO(orderWork);
		OrderDiscountCalcBO channelDiscount = orderDiscountCalcBService.getChannelDiscount(reCalcOrderAmountDIO.getDiscountConditionParamsDIO(), addDiscountDTO.getAmount(), null);
		if (Objects.nonNull(channelDiscount) && CollectionUtil.isNotNullOrEmpty(channelDiscount.getOrderDiscountList())) {
			log.info("渠道优惠-入库=>{}", JSON.toJSONString(channelDiscount));
			OrderDiscount orderDiscount = channelDiscount.getOrderDiscountList().get(0);
			orderDiscount.setOrderId(addDiscountDTO.getOrderId());
			orderDiscount.setRemark(String.format("渠道优惠：%s元", MoneyUtil.parseToYuanStr(addDiscountDTO.getAmount())));
			orderDiscount.setCreateTime(new Date());
			orderDiscountService.insert(orderDiscount);
			log.info("渠道优惠-核销=>{}", JSON.toJSONString(channelDiscount));
			try {
				Integer originalAmount = Optional.ofNullable(orderWork.getOriginalAmount()).orElse(0);
				orderDiscountCalcBService.receiptConsumeDiscount(channelDiscount.getReceiptConsumeDiscountList(), 1, originalAmount, orderWork, null,GlobalConsts.SPLITER_EMPTY,GlobalConsts.SPLITER_EMPTY);
			} catch (Exception e) {
				log.error("渠道优惠-核销失败", e.getMessage(), e);
			}
		} else {
			log.info("渠道优惠-未返回=>{}", addDiscountDTO);
			return;
		}

		int totalDiscountAmount = Optional.ofNullable(orderWork.getDiscountAmount()).orElse(0) + addDiscountDTO.getAmount();
		WorkAmountDTO workAmountDTO = new WorkAmountDTO();
		workAmountDTO.setOrderId(addDiscountDTO.getOrderId());
		workAmountDTO.setWorkId(addDiscountDTO.getWorkId());
		workAmountDTO.setAmount(totalDiscountAmount);
		log.info("渠道优惠-更新金额=>{}", workAmountDTO);
		orderWorkAmountBService.updateDiscountAmount(workAmountDTO);
	}


	@Override
	public void saveOrderDiscountList(List<OrderDiscount> orderDiscountList, Long orderId) throws OmsBaseException {
		if (CollectionUtils.isNotEmpty(orderDiscountList)) {
			// 校验是否有金额为负的优惠
			boolean present = orderDiscountList.stream().filter(orderDiscount -> Objects.nonNull(orderDiscount.getAmount()) && orderDiscount.getAmount() < 0)
					.findAny().isPresent();
			if (present) {
				throw new OmsBaseException(OmsErrorCodeConsts.ERROR_DISCOUNT_AMOUNT_IS_NEGATIVE, "优惠金额不能为负");
			}
		}

		// 先删后增
		orderDiscountService.deleteByOrderId(orderId);
		if (log.isDebugEnabled()) {
			log.debug("saveOrderDiscountList 删除优惠：orderId:{}",orderId);
		}
		if (orderDiscountList.isEmpty()) {
			return;
		}
		orderDiscountList.forEach(e -> e.setOrderId(orderId));
		if (log.isDebugEnabled()) {
			log.debug("saveOrderDiscountList 删除优惠并新增：orderId:{},orderDiscountList:{}",orderId,orderDiscountList);
		}
		orderDiscountService.insertOrderDiscountList(orderDiscountList);
	}

	/**
	 * 原有工单优惠转化成优惠计算dto
	 * @param orderId
	 * @return
	 */
	private List<CalculateDiscountListDTO> covertOrigDiscounts(Long orderId) {
		// 原有工单优惠
		List<OrderDiscount> orderDiscounts = orderDiscountService.listByOrderId(orderId);
		List<CalculateDiscountListDTO> calculateDiscountListDTOS = Lists.newArrayList();
		if (!CollectionUtils.isEmpty(orderDiscounts)) {
			calculateDiscountListDTOS = BeanMapper.mapList(orderDiscounts, CalculateDiscountListDTO.class);
		}
		return calculateDiscountListDTOS;
	}


	/**
	 *
	 * @param orderDTO
	 * @return true 可用，不修改 false 不可用，需要删除次卡相关信息
	 */
	public boolean checkTimesCoupCanUse(OrderDTO orderDTO) throws OmsBaseException {
		OrderWork orderWork = orderDTO.getOrderWork();
		OrderWork dbOrderWork = orderDTO.getDbOrderWork();
		OrderDetail orderDetail = orderDTO.getOrderDetail();
		List<OrderProduct> orderProductList = orderDTO.getOrderProductList();
		List<OrderDiscount> orderDiscounts = orderDTO.getOrderDiscountList();

		String coupCode ;
		Optional<OrderDiscount> discountOp = orderDiscounts.stream().filter(discount -> {
			return Objects.equals(discount.getCateg(), DiscountTypeEnums.DISCOUNT_COUP_ONCECARD.getSubType());
		}).findFirst();
		if (discountOp.isPresent()) {
			coupCode = discountOp.get().getItemCode();
		} else {
			return true;
		}

		CoupOnceCardCheckDIO dio = new CoupOnceCardCheckDIO();
		dio.setOrderId(dbOrderWork.getOrderId());
		dio.setCoupCode(coupCode);
		dio.setUserId(orderWork.getUserId());
		dio.setLongitude(new BigDecimal(String.valueOf(orderDetail.getLongitude())));
		dio.setLatitude(new BigDecimal(String.valueOf(orderDetail.getLatitude())));
		dio.setProductId(orderProductList.get(0).getShowProductId());
		dio.setCityId(orderWork.getCityId());
		dio.setChannelId(orderWork.getChannelId());
		dio.setBizType(orderWork.getBizType());
		dio.setPrice(dbOrderWork.getOriginalAmount());
		dio.setTariffOnoff(Objects.equals(orderWork.getServItemType(), OrderConsts.SERVICE_ITEM_TYPE_FAULT) ? com.zmn.consts.GlobalConsts.YES : com.zmn.consts.GlobalConsts.NO);
		dio.setUntariffOnoff(!Objects.equals(orderWork.getServItemType(), OrderConsts.SERVICE_ITEM_TYPE_FAULT) ? com.zmn.consts.GlobalConsts.YES : com.zmn.consts.GlobalConsts.NO);
		dio.setExtendedWarranty(dbOrderWork.getMember());
		dio.setUnExtendedWarranty(Objects.equals(dbOrderWork.getMember(), com.zmn.consts.GlobalConsts.YES) ? com.zmn.consts.GlobalConsts.NO : com.zmn.consts.GlobalConsts.YES);
		dio.setTaximeter3(servItemBService.isQuotationThreeType(orderWork.getServItemType(), orderWork.getFinalPrice()) ? com.zmn.consts.GlobalConsts.YES : com.zmn.consts.GlobalConsts.NO);

		String limitAddress = String.format("%s%s%s%s%s", orderDetail.getProvinceName(), orderDetail.getCityName(), orderDetail.getCountyName(), Optional.ofNullable(orderDetail.getStreet()).orElse(""), orderDetail.getAddress());
		dio.setLimitAddress(limitAddress);
		ResponseDTO<Boolean> responseDTO = coupOnceCardRemoteService.checkOnceCardEffectiveOrNot(dio);

		log.info("[{}]校验次卡优惠券是否继续可用 入参：{}, 出参:{}", orderWork.getWorkId(), JSON.toJSONString(dio), JSON.toJSONString(responseDTO));
		if (!responseDTO.isSuccess()) {
			log.error("校验优惠券是否可用失败：{}", responseDTO.getMessage());
			throw new OmsBaseException("校验优惠券是否可用失败，请稍后重试");
		}

		return responseDTO.getData();
	}

	@Override
	public OrderDiscountCompareBO refreshOrderDiscount(OmsOperator operator) throws OmsBaseException {
		OrderWork orderWork = orderWorkService.findOrderWorkByKeySrcMaster(operator.getOrderId(), operator.getWorkId());
		if (Objects.isNull(orderWork)) {
			throw new OmsBaseException("订单不存在");
		}

		OrderDiscountCompareBO compareBO = new OrderDiscountCompareBO();

		if (!Objects.equals(orderWork.getResultStatus(), OrderStatusConsts.WORK_RESULT_DOING)
				|| orderWork.getStatus() >= OrderStatusConsts.WORK_STATUS_COMPLETE) {
			return compareBO;
		}
		if (Objects.equals(orderWork.getMasterPayStatus(), PayConsts.PAY_STATUS_DONE)) {
			return compareBO;
		}
		//41361,32794  58渠道刷新优惠不处理
		if (Objects.equals(orderWork.getChannelId(), 41361) || Objects.equals(orderWork.getChannelId(), 32794)) {
			return compareBO;
		}
		ReCalcOrderAmountDIO calcOrderAmountDIO = orderAmountCalcBService.getReCalcOrderAmountDIO(orderWork);

		compareBO.setOldDiscountList(calcOrderAmountDIO.getOrderDiscountList());

		// 只有人工优惠不刷新取优惠
		List<OrderDiscount> orderDiscountList = calcOrderAmountDIO.getOrderDiscountList();
		if (Objects.nonNull(orderDiscountList)) {
			boolean onlyArtificial = orderDiscountList.stream()
					.anyMatch(e -> Objects.equals(e.getCateg(), DiscountConsts.DISCOUNT_CATEG_ARTIFICIAL));
			if (onlyArtificial) {
				log.info("[{}}工程师获取最新优惠---只有人工优惠不刷新", operator.getWorkId());
				return compareBO;
			}
		}

		OrderAmountCalcBO orderAmountCalcBO = orderAmountCalcBService.reCalcOrderAmountAndDiscountAmount(TriggerAmountChangeEnum.REFRESH_DISCOUNT, calcOrderAmountDIO, null);
		//主从并发问题，发现订单已完成
		if (NumberUtil.isNotNullOrZero(orderWork.getChannelPrepayAmount()) || NumberUtil.isNotNullOrZero(orderAmountCalcBO.getChannelPrepayAmount())) {
			OrderWork orderWorkByKeySrcMaster = orderWorkService.findOrderWorkByKeySrcMaster(orderWork.getOrderId(), orderWork.getWorkId());
			if (!Objects.equals(orderWorkByKeySrcMaster.getResultStatus(), OrderStatusConsts.WORK_RESULT_DOING)
					|| orderWorkByKeySrcMaster.getStatus() >= OrderStatusConsts.WORK_STATUS_COMPLETE) {
				log.info("[{}}工程师获取最新优惠---查主库订单已完成，不处理", operator.getWorkId());
				return compareBO;
			}
		}
		orderWorkAmountBService.updateAmountAndDiscount(orderAmountCalcBO, orderWork);

		if (Objects.nonNull(orderAmountCalcBO.getOrderDiscountCalcBO()) && CollectionUtil.isNotNullOrEmpty(orderAmountCalcBO.getOrderDiscountCalcBO().getOrderDiscountList())) {
			compareBO.setNewDiscountList(orderAmountCalcBO.getOrderDiscountCalcBO().getOrderDiscountList());
		}

		return compareBO;
	}
}
